package com.geor.gcv.core.mat;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.nutz.lang.Lang;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.slf4j.Logger;

import com.geor.common.log.Loggers;
import com.geor.gcv.core.model.Area;
import com.geor.gcv.core.model.SharpArea;

/**
 * 混合切割（投影&瀑布）
 * 投影切分可以用默认的实现，也可以注入其他实现
 * 
 * @author geor.yuli
 *
 */
public class CellChipperComboImpl implements CellChipper {

	static Logger log = Loggers.get();

	/** 默认的宽高比例, 大于该比例判断字符数量大于1 */
	float WIDTH_HEIGHT_PERCENT = 1f;

	CellChipper cellChipperBase;
	CellChipper cellChipperDefault = new CellChipperFallImpl();

	public CellChipperComboImpl() {
		// 默认的X、Y轴投影切分实现，也可以使用CellChipperSimpleImpl
		this.cellChipperBase = new CellChipperAxisImpl();
	}
	
	public CellChipperComboImpl(float percent) {
		this();
		this.WIDTH_HEIGHT_PERCENT = percent;
	}
	
	public CellChipperComboImpl(CellChipper cellChipper, float percent) {
		this.cellChipperBase = cellChipper;
		this.WIDTH_HEIGHT_PERCENT = percent;
	}
	

	@Override
	public List<Area> execute(Mat mat) throws Exception {
		List<Area> chipAreas = new ArrayList<Area>();
		List<Area> areas = cellChipperBase.execute(mat);
		
		try {
			// 粘连部分再次切分
			Iterator<Area> iterator = areas.iterator();
			for (; iterator.hasNext();) {
				Area area = iterator.next();
				float percent = (float) area.getWidth() / (float) area.getHeight();
				if (percent > WIDTH_HEIGHT_PERCENT) {
					log.trace(String.format("to chip area: {x:%d}", area.getX()));
					iterator.remove();
					chipAreas.add(area);
				}
			}

			for (Area chipArea : chipAreas) {
				Area cutArea = new Area(chipArea.getX(), 0, chipArea.getWidth(), mat.rows());
				Mat shard = Mats.screenShoot(mat, cutArea);
				Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 2));
				try {
					// 膨胀
					Imgproc.morphologyEx(shard, shard, Imgproc.MORPH_DILATE, element);
					List<Area> shardAreas = cellChipperDefault.execute(shard);
					// show(shard, shardAreas);
					if (!Lang.isEmpty(shardAreas)) {
						Iterator<Area> shardAreaIterator = shardAreas.iterator();
						for (; shardAreaIterator.hasNext();) {
							Area shardArea = shardAreaIterator.next();
							// 切分验证
							float percent = (float) shardArea.getWidth() / (float) shardArea.getHeight();
							if (percent > WIDTH_HEIGHT_PERCENT) {
								shardAreaIterator.remove();
								SharpArea area0 = new SharpArea(chipArea.getX() + shardArea.getX(), chipArea.getY(),
										shardArea.getWidth()/2, chipArea.getHeight());
								SharpArea area1 = new SharpArea(chipArea.getX() + shardArea.getX() + shardArea.getWidth()/2 + 1, chipArea.getY(),
										shardArea.getWidth()/2, chipArea.getHeight());
								areas.add(area0);
								areas.add(area1);
							}
						}
					}
					
					if (!Lang.isEmpty(shardAreas)) {
						// 位置换算
						for (Area shardArea : shardAreas) {
							if (shardArea.getWidth() >= 3) {
								shardArea.setX(shardArea.getX() + chipArea.getX());
								shardArea.setY(chipArea.getY());
								areas.add(shardArea);
							}
						}
					}
					shardAreas.clear();

				} finally {
					Mats.releseMat(shard, element);
				}
			}

			iterator = areas.iterator();
			for (; iterator.hasNext();) {
				if (iterator.next().getWidth() < 3) {
					iterator.remove();
				}
			}

			// 重新排序
			Collections.sort(areas, new Comparator<Area>() {
				@Override
				public int compare(Area a1, Area a2) {
					return a1.getX() - a2.getX();
				}
			});

			for (int i = 0; i < areas.size(); i++) {
				Area area = areas.get(i);
				if (area instanceof SharpArea) {
					continue;
				}
				SharpArea sharpArea = new SharpArea(area.getX(), area.getY(), area.getWidth(), area.getHeight());
				sharpArea.setP1(area.getX(), 0);
				sharpArea.setP2(area.getX() + area.getWidth(), 0);
				areas.set(i, sharpArea);
			}

			return areas;
		} finally {
			chipAreas.clear();
		}
	}

	@Override
	public void setParams(Properties properties) {
		// TODO Auto-generated method stub

	}

}
